import os
import subprocess
import ctypes
import sys
import logging  # 新增导入
from plyer import notification

# 配置日志系统
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("log.txt", encoding="utf-8"),  # 文件日志
        # logging.StreamHandler(),  # 控制台日志
    ],
)


def show_notification(title, message, timeout=1):
    notification.notify(
        title=title,
        message=message,
        timeout=timeout,
    )  # type: ignore


# def get_terminal_encoding():
#     """获取终端编码，优先使用系统标准输出编码"""
#     return sys.stdout.encoding or "utf-8"


def is_admin():
    """检查管理员权限"""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except Exception as e:
        logging.error(f"检查管理员权限时发生异常: {str(e)}")  # 改为日志记录
        return False


def run_powershell(command, capture_output=True):
    """通用执行PowerShell命令的函数"""
    cmd = ["powershell.exe", "-Command", command]

    # Windows下隐藏PowerShell窗口配置
    if sys.platform == "win32":
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE
        creationflags = subprocess.CREATE_NO_WINDOW
    else:
        startupinfo = None
        creationflags = 0

    try:
        result = subprocess.run(
            cmd,
            capture_output=capture_output,
            text=True,
            errors="replace",
            startupinfo=startupinfo,
            creationflags=creationflags,
        )
        logging.debug(f"执行命令: {command}")
        if result.stdout:
            logging.debug(f"标准输出:\n{result.stdout}")
        if result.stderr:
            logging.debug(f"标准错误:\n{result.stderr}")
        return result
    except UnicodeDecodeError as e:
        logging.error(f"解码输出时出现编码错误: {str(e)}")
        return subprocess.CompletedProcess(cmd, 1, None, None)
    except Exception as e:
        logging.error(f"执行命令时发生未知错误: {str(e)}")
        return subprocess.CompletedProcess(cmd, 1, None, None)


def install_sshd():
    """安装SSHD服务"""
    logging.info("正在安装OpenSSH服务器...")  # 改为日志记录
    result = run_powershell(
        "Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0"
    )

    if result.returncode != 0:
        if "already installed" in result.stdout:
            logging.info("SSHD组件已存在，跳过安装")  # 改为日志记录
            return
        logging.error(
            "安装失败，错误信息：\n%s",  # 改为日志记录
            result.stderr if result.stderr else result.stdout,
        )
        sys.exit(1)
    logging.info("SSHD安装成功")  # 改为日志记录


def configure_firewall():
    """配置防火墙规则"""
    logging.info("\n配置防火墙...")  # 改为日志记录
    result = run_powershell(
        'if (-not (Get-NetFirewallRule -Name "OpenSSH-Server" -ErrorAction SilentlyContinue)) {'
        'New-NetFirewallRule -Name "OpenSSH-Server" -DisplayName "OpenSSH Server" '
        "-Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22"
        "}"
    )

    if result.returncode == 0:
        logging.info("防火墙规则已配置")  # 改为日志记录
    else:
        logging.warning(
            "防火墙配置状态：\n%s",  # 改为日志记录
            result.stderr if result.stderr else "可能已存在相关规则",
        )


def update_sshd_config():
    """更新SSH配置文件"""
    logging.info("\n更新SSHD配置文件...")  # 改为日志记录
    config_dir = r"C:\ProgramData\ssh"
    config_path = os.path.join(config_dir, "sshd_config")
    os.makedirs(config_dir, exist_ok=True)

    try:
        # 确保配置文件存在
        if not os.path.exists(config_path):
            open(config_path, "w").close()

        dir_path = os.path.dirname(os.path.realpath(__file__))
        with open(dir_path + "\\sshd_config.bak", "r", encoding="utf-8") as f:
            new_config = f.read()

        # 读取现有配置
        with open(config_path, "r", encoding="utf-8") as f:
            content = f.read()

        # 检查是否需要更新
        if "SyslogFacility LOCAL0" not in content:
            with open(config_path, "a", encoding="utf-8") as f:
                f.write(new_config)
            logging.info("配置文件已更新")  # 改为日志记录
        else:
            logging.info("检测到现有配置，跳过更新")  # 改为日志记录
    except Exception as e:
        logging.error(f"配置文件操作失败: {str(e)}")  # 改为日志记录
        raise


def manage_service():
    """服务管理"""
    logging.info("\n配置SSHD服务...")  # 改为日志记录

    try:
        # 检查服务状态
        status_check = run_powershell(
            "Get-Service sshd | Select-Object -ExpandProperty Status"
        )
        if "Running" in status_check.stdout:
            logging.info("服务已在运行状态")  # 改为日志记录
        else:
            start_result = run_powershell("Start-Service sshd")
            if start_result.returncode == 0:
                logging.info("服务已成功启动")  # 改为日志记录

        # 设置自启动
        startup_check = run_powershell(
            "Get-Service sshd | Select-Object -ExpandProperty StartType"
        )
        if "Automatic" not in startup_check.stdout:
            set_result = run_powershell("Set-Service -Name sshd -StartupType Automatic")
            if set_result.returncode == 0:
                logging.info("已设置开机自动启动")  # 改为日志记录
        else:
            logging.info("开机自启动已配置")  # 改为日志记录
    except Exception as e:
        logging.error(f"服务管理失败: {str(e)}")  # 改为日志记录
        raise


def main():
    try:
        if not is_admin():
            logging.error("请以管理员身份重新运行此脚本！")  # 改为日志记录
            logging.error("右键点击脚本文件，选择'以管理员身份运行'")
            sys.exit(1)

        update_sshd_config()
        show_notification("tip", "done")

        install_sshd()
        configure_firewall()

        manage_service()

        # logging.info("\n" + "=" * 50)  # 改为日志记录
        # logging.info("配置完成！请验证连接：")
        # logging.info("1. 检查IP地址：")
        # run_powershell(
        #     "Get-NetIPAddress | Where-Object { $_.AddressFamily -eq 'IPv4' } | Select-Object IPAddress",
        #     False,
        # )
        # logging.info("\n2. 测试连接命令：ssh 用户名@你的IP")
        show_notification("tip", "done2")

    except Exception as e:
        logging.exception(
            f"执行过程中出现意外错误: {str(e)}"
        )  # 使用exception记录完整堆栈
        sys.exit(1)


if __name__ == "__main__":
    main()
